﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using System.IO;
using System.Text.RegularExpressions;

namespace DARE
{
   #if !WINDOWS_PHONE
    /// <summary>
    /// When adding a .xap to the project, dont forget to set the "copy to destination directory" 
    /// property. Otherwise the audio manager will not be able to access the list of available sounds.
    /// WARNING: all the create methods can only be called once, the sound list will not be reset otherwise.
    /// </summary>
    public class CSoundMgr
    {

    #region fields


        private AudioEngine m_audioEngine;
        private WaveBank m_waveBank;
        private SoundBank m_soundBank;
        private AudioListener m_audioListener;
        private List<CSound> m_Sounds;
        private int m_nCreated;
        private int m_maxSounds;
        private int m_playingSounds;
        private List<string> m_soundList;
        private string m_soundDirectory = "";
        private string m_audioEnginePath;

    #endregion

    #region properties

        public List<string> XAPSoundList
        {
            get { return m_soundList; }
        }

        public SoundBank SoundBank
        {
            get { return m_soundBank; }
        }

        public WaveBank WaveBank
        {
            get { return m_waveBank; }
        }

        public AudioEngine AudioEngine
        {
            get { return m_audioEngine; }
        }

        public AudioListener AudioListener // should be set in the camera manager
        {
            get { return m_audioListener; }
        }

        public string SoundDirectory
        {
            get { return m_soundDirectory; }
            set { m_soundDirectory = value.EndsWith("/") ? value : value + "/"; }
        }

    #endregion

    #region ctor

        public CSoundMgr()
        {
            Initialize();
        }

        private void Initialize()
        {
            m_audioEngine = null;
            m_waveBank = null;
            m_soundBank = null;
            m_audioListener = new AudioListener();
            m_Sounds = new List<CSound>();
            m_nCreated = 0;
            m_maxSounds = 8;
            m_playingSounds = 0;
            m_soundList = new List<string>();
        }

    #endregion

    #region create

        public bool InitSoundEngine(string audioEnginePath, string soundBankPath, string waveBankPath)
        {
            if (!audioEnginePath.Contains('.')) audioEnginePath = audioEnginePath.Split('.').ElementAt(0) + ".xgs";
            if (!soundBankPath.Contains('.'))   soundBankPath = soundBankPath.Split('.').ElementAt(0) + ".xsb";
            if (!waveBankPath.Contains('.'))    waveBankPath = waveBankPath.Split('.').ElementAt(0) + ".xwb";
            return CreateAudioEngine(m_audioEnginePath = m_soundDirectory + audioEnginePath) && 
                CreateSoundBank(m_soundDirectory + soundBankPath) && 
                CreateWaveBank(m_soundDirectory + waveBankPath);
        }

        /// <summary>
        /// Need to be call in first, should display warnings if the path is wrong
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool CreateAudioEngine(string path)
        {
            Console.WriteLine("Create the audio engine from: " + path);
            if (!File.Exists(path))
                throw new Exception("No audio engine found at: " + path + " !");
            m_audioEngine = new AudioEngine(path);
            if (m_audioEngine == null)
                throw new Exception("Error creating the Audio Engine");
            CreateSoundDictionary();
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool CreateSoundBank(string path)
        {
            Console.WriteLine("Create the sound bank from: " + path);   
            if (!File.Exists(path) || (m_audioEngine == null))
                throw new Exception("No sound bank found at: " + path + " !");
            m_soundBank = new SoundBank(m_audioEngine, path);
            if (m_soundBank == null)
                throw new Exception("Error creating the Sound Bank");
            CreateSoundDictionary();
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool CreateWaveBank(string path)
        {
            Console.WriteLine("Create the wave bank from: " + path);
            if (!File.Exists(path) || (m_audioEngine == null))
                throw new Exception("No wave bank found at: " + path + " !");
            m_waveBank = new WaveBank(m_audioEngine, path);
            if (m_waveBank == null)
                throw new Exception("Error creating the Wave Bank");
            CreateSoundDictionary();
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        private void CreateSoundDictionary()
        {
            m_nCreated += 1;
            if (m_nCreated == 3)
                ExtractSoundNamesFromXap();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private void ExtractSoundNamesFromXap()
        {
            StreamReader sr = new StreamReader(m_audioEnginePath.Replace(".xgs", ".xap"));
            string s = sr.ReadToEnd();
            Regex rx = new Regex(@"File[ ]?=[ ]?(.*)\.wav", RegexOptions.IgnoreCase);
            MatchCollection matches = rx.Matches(s);

            foreach (Match match in matches)
            {
                GroupCollection gc = match.Groups;
                Group g = gc[1];
                m_soundList.Add(g.ToString());
            }
            sr.Close();
        }

    #endregion

    #region methods

        public void AddSound(CSound sound)
        {
            if (sound != null)
                m_Sounds.Add(sound);
        }

        public void RemoveSound(CSound sound)
        {
            if (sound != null)
            {
                if (sound.Entity != null)
                    sound.Entity.DetachSound(sound);
                sound.Stop();
                m_Sounds.Remove(sound);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameTime"></param>
        /// <returns></returns>
        public bool Update(GameTime gameTime)
        {
            int playingSound = 0;

            if ((m_audioEngine == null) || (m_soundBank == null) || (m_waveBank == null))
                return false;
            foreach (CSound sound in m_Sounds)
            {
                if (sound.Node.Render3D == null)
                    continue;

                CRender3D render = sound.Node.Render3D;

                if (render == null)
                    throw new Exception("No render attached to the node, cannot play sound " + sound.Name + " !");

                // the render is not active, dont play the sound
                if (!CDare.Instance.SceneMgr.ActiveScene.ActiveRender3D.Equals(render))
                    continue;

                CCamera camera = sound.Node.Render3D.CameraMgr.ActiveCamera;

                if (camera != null)
                {
                    m_audioListener.Position = camera.Position;
                    m_audioListener.Forward = camera.Heading;
                    m_audioListener.Up = camera.Up;
                    if (sound.IsTriggered)
                        playingSound += 1;
                }
            }
            m_audioEngine.Update();
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Unload()
        {
            m_Sounds.Clear();
            m_nCreated = 0;
        }

        public void PlaySound(CSound sound)
        {
            if (sound != null)
                sound.Play();
        }

    #endregion

    }
#endif
}
